<template>
  <div class="test-page">
    <div class="test-header">
      <h1>i3wm-style Tiling Window Manager</h1>
      <div class="test-controls">
        <button @click="addWindow" class="btn-primary">添加窗口</button>
        <button @click="clearWindows" class="btn-secondary">清空窗口</button>
      </div>
    </div>
    
    <!-- 窗口容器 -->
    <div class="window-container" ref="containerRef">
      <!-- 窗口 -->
      <div
        v-for="win in visibleWindows"
        :key="win.id"
        :data-window-id="win.id"
        class="window"
        :class="{ 
          'window-highlighted': highlightedWindowId === win.id,
          'window-dragging': win.isDragging,
          'window-hovered': hoveredWindowId === win.id,
          'window-editing': editingWindowId === win.id
        }"
        :style="getWindowStyle(win)"
        @mousemove="handleWindowMouseMove($event, win)"
        @mouseleave="handleWindowMouseLeave(win.id)"
      >
        <!-- 检测区域可视化 -->
        <div
          class="hover-detection-zone"
          :style="getDetectionZoneStyle(win)"
          @mousemove="handleDetectionZoneMouseMove($event, win)"
          @mouseleave="handleWindowMouseLeave(win.id)"
        ></div>
        
        <!-- iOS灵动岛工具栏 -->
        <div
          class="window-island"
          :class="{ 
            'island-expanded': editingWindowId === win.id,
            'island-hovered': hoveredWindowId === win.id
          }"
          @pointerdown="handleIslandPointerDown($event, win)"
        >
          <div class="island-content">
            <button
              v-if="editingWindowId !== win.id"
              class="island-button island-edit"
              type="button"
              @click.stop="toggleUrlEditor(win)"
              :aria-pressed="editingWindowId === win.id"
              title="编辑URL"
            >
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                <path d="M7.5 1.5L8.5 2.5L3 8H2V7L7.5 1.5Z" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </button>
            <button
              v-if="editingWindowId !== win.id"
              class="island-button island-refresh"
              type="button"
              @click.stop="refreshWindow(win.id)"
              title="刷新窗口"
            >
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                <path d="M8.5 2.5C8 1.5 7 1 5.5 1C3.5 1 2 2.5 2 4.5M1.5 2.5L2 1.5L3 2.5M1.5 7.5C2 8.5 3 9 4.5 9C6.5 9 8 7.5 8 5.5M8.5 7.5L8 8.5L7 7.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </button>
            <button
              v-if="editingWindowId !== win.id"
              class="island-button island-close"
              type="button"
              @click.stop="removeWindow(win.id)"
              title="关闭窗口"
            >
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                <circle cx="5" cy="5" r="4.5" stroke="currentColor" stroke-width="1.2" fill="none"/>
                <path d="M3.5 3.5L6.5 6.5M6.5 3.5L3.5 6.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
              </svg>
            </button>
          </div>

          <div
            class="island-editor"
            :class="{ 'island-editor-visible': editingWindowId === win.id }"
            @click.stop
          >
            <input
              :value="editUrlValue"
              class="island-input"
              type="text"
              placeholder="输入网址"
              :data-url-editor="win.id"
              @input="handleUrlInputChange"
              @keydown="handleUrlInputKeydown"
            />
            <button
              class="island-button island-action island-commit"
              type="button"
              @click.stop="commitUrlEdit"
              title="应用"
            >
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                <path d="M2 5L4.5 7.5L8 3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </button>
            <button
              class="island-button island-action island-cancel"
              type="button"
              @click.stop="cancelUrlEdit"
              title="取消"
            >
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
                <path d="M2 2L8 8M8 2L2 8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
              </svg>
            </button>
          </div>
        </div>
        
        <!-- 内容区 (iframe) -->
        <div class="window-content">
          <iframe 
            :key="win.id"
            :src="win.url" 
            frameborder="0"
            class="window-iframe"
          ></iframe>
        </div>
        
        <!-- 调整大小的句柄 -->
        <div 
          v-if="canResizeRight(win)"
          class="resize-handle resize-handle-right"
          @pointerdown="handleResizePointerDown($event, win, 'right')"
        ></div>
        <div 
          v-if="canResizeBottom(win)"
          class="resize-handle resize-handle-bottom"
          @pointerdown="handleResizePointerDown($event, win, 'bottom')"
        ></div>
      </div>
      
      <!-- 拖拽指示器 -->
      <div 
        v-if="dragIndicator.visible"
        class="drag-indicator"
        :style="getDragIndicatorStyle()"
      >
        {{ dragIndicator.title }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'

// ==================== 类型定义 ====================
type TileIndex = 0 | 1 | 2 | 3

interface WindowData {
  id: string
  title: string
  url: string
  tileIndex: TileIndex
  isDragging: boolean
  // 布局属性（由计算得出）
  x: number
  y: number
  width: number
  height: number
}

interface Layout {
  cols: number
  rows: number
  tiles: Array<{
    index: TileIndex
    x: number
    y: number
    width: number
    height: number
  }>
}

// ==================== 常量与工具函数 ====================
const EPSILON = 0.5
const MIN_WINDOW_WIDTH = 120
const MIN_WINDOW_HEIGHT = 120

const approxEqual = (a: number, b: number, tolerance = EPSILON) => Math.abs(a - b) <= tolerance

interface ResizeTarget {
  id: string
  element: HTMLElement
  initialX: number
  initialY: number
  initialWidth: number
  initialHeight: number
}

// ==================== 状态管理 ====================
const containerRef = ref<HTMLElement | null>(null)
const windows = ref<Map<string, WindowData>>(new Map())
const windowCounter = ref(0)
const isDragging = ref(false)
const draggingWindowId = ref<string | null>(null)
const highlightedWindowId = ref<string | null>(null)
const hoveredWindowId = ref<string | null>(null)
const editingWindowId = ref<string | null>(null)
const editUrlValue = ref('')
const isResizing = ref(false)
const resizingWindowId = ref<string | null>(null)

// 拖拽指示器
const dragIndicator = ref({
  visible: false,
  x: 0,
  y: 0,
  width: 0,
  height: 0,
  title: ''
})

const getWindowElement = (id: string): HTMLElement | null => {
  if (typeof window === 'undefined') return null
  return document.querySelector(`[data-window-id="${id}"]`) as HTMLElement | null
}

const getAllWindowsArray = () => Array.from(windows.value.values())

// 从 DOM 读取窗口位置和大小
const getWindowRect = (win: WindowData) => {
  const element = getWindowElement(win.id)
  if (!element || !containerRef.value) return null
  const rect = element.getBoundingClientRect()
  const containerRect = containerRef.value.getBoundingClientRect()
  return {
    x: rect.left - containerRect.left,
    y: rect.top - containerRect.top,
    width: rect.width,
    height: rect.height
  }
}

const findColumnPeers = (win: WindowData) => {
  const container = containerRef.value
  if (!container) return []
  const winRect = getWindowRect(win)
  if (!winRect) return []
  
  const left = winRect.x
  const right = winRect.x + winRect.width
  return getAllWindowsArray().filter(other => {
    const otherRect = getWindowRect(other)
    if (!otherRect) return false
    return approxEqual(otherRect.x, left) && approxEqual(otherRect.x + otherRect.width, right)
  })
}

const findColumnAfter = (win: WindowData) => {
  const container = containerRef.value
  if (!container) return []
  const winRect = getWindowRect(win)
  if (!winRect) return []
  
  const boundary = winRect.x + winRect.width
  return getAllWindowsArray().filter(other => {
    if (other.id === win.id) return false
    const otherRect = getWindowRect(other)
    if (!otherRect) return false
    return approxEqual(otherRect.x, boundary)
  })
}

const findRowPeers = (win: WindowData) => {
  const container = containerRef.value
  if (!container) return []
  const winRect = getWindowRect(win)
  if (!winRect) return []
  
  const top = winRect.y
  const bottom = winRect.y + winRect.height
  return getAllWindowsArray().filter(other => {
    const otherRect = getWindowRect(other)
    if (!otherRect) return false
    return approxEqual(otherRect.y, top) && approxEqual(otherRect.y + otherRect.height, bottom)
  })
}

const findRowBelow = (win: WindowData) => {
  const container = containerRef.value
  if (!container) return []
  const winRect = getWindowRect(win)
  if (!winRect) return []
  
  const boundary = winRect.y + winRect.height
  return getAllWindowsArray().filter(other => {
    if (other.id === win.id) return false
    const otherRect = getWindowRect(other)
    if (!otherRect) return false
    return approxEqual(otherRect.y, boundary)
  })
}

// ==================== 计算属性 ====================
// 不排序，保持插入顺序，避免因排序改变导致 DOM 重新排列
const visibleWindows = computed(() => {
  return Array.from(windows.value.values())
})

const containerRect = ref({ width: 0, height: 0 })

// ==================== 布局计算 ====================
const calculateLayout = (windowCount: number): Layout => {
  const { width, height } = containerRect.value
  
  if (windowCount === 0) {
    return { cols: 1, rows: 1, tiles: [] }
  }
  
  if (windowCount === 1) {
    return {
      cols: 1,
      rows: 1,
      tiles: [{
        index: 0 as TileIndex,
        x: 0,
        y: 0,
        width,
        height
      }]
    }
  }
  
  if (windowCount === 2) {
    return {
      cols: 2,
      rows: 1,
      tiles: [
        { index: 0 as TileIndex, x: 0, y: 0, width: width / 2, height },
        { index: 1 as TileIndex, x: width / 2, y: 0, width: width / 2, height }
      ]
    }
  }
  
  if (windowCount === 3) {
    // 左侧一个，右侧两个（上下分布）
    return {
      cols: 2,
      rows: 2,
      tiles: [
        { index: 0 as TileIndex, x: 0, y: 0, width: width / 2, height },
        { index: 1 as TileIndex, x: width / 2, y: 0, width: width / 2, height: height / 2 },
        { index: 2 as TileIndex, x: width / 2, y: height / 2, width: width / 2, height: height / 2 }
      ]
    }
  }
  
  // 4个窗口：2x2 网格
  return {
    cols: 2,
    rows: 2,
    tiles: [
      { index: 0 as TileIndex, x: 0, y: 0, width: width / 2, height: height / 2 },
      { index: 1 as TileIndex, x: width / 2, y: 0, width: width / 2, height: height / 2 },
      { index: 2 as TileIndex, x: 0, y: height / 2, width: width / 2, height: height / 2 },
      { index: 3 as TileIndex, x: width / 2, y: height / 2, width: width / 2, height: height / 2 }
    ]
  }
}

// 应用布局到窗口（使用 CSS 绝对定位，不更新响应式数据）
const applyLayout = (useTransition = false) => {
  if (isDragging.value || isResizing.value) return
  
  const windowCount = windows.value.size
  const layout = calculateLayout(windowCount)
  const container = containerRef.value
  if (!container) return
  
  // 直接操作 DOM，使用 CSS 绝对定位更新位置和大小（不触发 Vue 重新渲染）
  windows.value.forEach((win) => {
    const tile = layout.tiles.find(t => t.index === win.tileIndex)
    if (tile) {
      const element = getWindowElement(win.id)
      if (!element) return
      
      // 获取当前元素的实际位置（从 DOM 读取）
      const currentRect = element.getBoundingClientRect()
      const containerRect = container.getBoundingClientRect()
      const currentX = currentRect.left - containerRect.left
      const currentY = currentRect.top - containerRect.top
      
      // 计算需要移动的距离
      const deltaX = tile.x - currentX
      const deltaY = tile.y - currentY
      const deltaWidth = tile.width - currentRect.width
      const deltaHeight = tile.height - currentRect.height
      
      // 如果有位置或大小变化，使用 CSS transition 实现平滑动画
      if (useTransition && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1 || Math.abs(deltaWidth) > 0.1 || Math.abs(deltaHeight) > 0.1)) {
        // 设置 transition
        element.style.transition = 'left 0.3s ease-out, top 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out'
        
        // 更新位置和大小
        element.style.left = `${tile.x}px`
        element.style.top = `${tile.y}px`
        element.style.width = `${tile.width}px`
        element.style.height = `${tile.height}px`
        
        // 动画完成后清除 transition
        setTimeout(() => {
          element.style.transition = ''
        }, 300)
      } else {
        // 没有动画，直接更新
        element.style.left = `${tile.x}px`
        element.style.top = `${tile.y}px`
        element.style.width = `${tile.width}px`
        element.style.height = `${tile.height}px`
      }
    }
  })
}

// 获取窗口样式（只返回基础样式，位置和大小由 CSS 直接控制）
const getWindowStyle = (_win: WindowData) => {
  // 只返回基础样式，位置和大小通过直接操作 DOM 的 style 属性来更新
  // 这样不会触发 Vue 重新渲染
  return {
    position: 'absolute' as const,
    display: 'flex' as const
  }
}

// 获取检测区域样式（从 DOM 读取窗口大小）
const getDetectionZoneStyle = (win: WindowData) => {
  const topThreshold = 80  // 扩大高度到 80px
  // 从 DOM 读取窗口宽度，避免依赖响应式数据
  const element = getWindowElement(win.id)
  const width = element ? element.getBoundingClientRect().width : win.width
  const centerStart = width * 0.2  // 扩大水平范围到 20-80%
  const centerEnd = width * 0.8
  const zoneWidth = centerEnd - centerStart
  
  return {
    position: 'absolute' as const,
    left: `${centerStart}px`,
    top: '0',
    width: `${zoneWidth}px`,
    height: `${topThreshold}px`,
    pointerEvents: 'auto' as const,
    zIndex: 1
  }
}

// 获取拖拽指示器样式
const getDragIndicatorStyle = () => {
  return {
    left: `${dragIndicator.value.x}px`,
    top: `${dragIndicator.value.y}px`,
    width: `${dragIndicator.value.width}px`,
    height: `${dragIndicator.value.height}px`
  }
}

// ==================== 窗口管理 ====================
const addWindow = () => {
  if (windows.value.size >= 4) {
    alert('最多支持4个窗口')
    return
  }
  
  windowCounter.value++
  const id = `window-${windowCounter.value}`
  const tileIndex = windows.value.size as TileIndex
  
  // 根据窗口索引设置不同的默认 URL
  const defaultUrls = [
    'https://lazycat.cloud/',
    'https://lazycat.cloud/ai-pod',
    'https://lazycat.cloud/appstore',
    'https://lazycat.cloud/developer'
  ]
  const defaultUrl = defaultUrls[tileIndex] || 'https://lazycat.cloud/'
  
  const newWindow: WindowData = {
    id,
    title: `窗口 ${windowCounter.value}`,
    url: defaultUrl,
    tileIndex,
    isDragging: false,
    x: 0,
    y: 0,
    width: 0,
    height: 0
  }
  
  windows.value.set(id, newWindow)
  
  nextTick(() => {
    // 初始化窗口位置（使用 CSS 直接设置，不更新响应式数据）
    // applyLayout 会处理所有窗口的位置，包括新窗口
    applyLayout()
  })
}

const removeWindow = (windowId: string) => {
  windows.value.delete(windowId)
  
  if (editingWindowId.value === windowId) {
    cancelUrlEdit()
  }

  // 重新分配 tileIndex
  const remainingWindows = Array.from(windows.value.values()).sort((a, b) => a.tileIndex - b.tileIndex)
  remainingWindows.forEach((win, index) => {
    win.tileIndex = index as TileIndex
  })
  
  nextTick(() => {
    applyLayout()
  })
}

const refreshWindow = (windowId: string) => {
  const win = windows.value.get(windowId)
  if (!win) return
  
  // 通过重新设置 iframe 的 src 来刷新
  nextTick(() => {
    const iframe = document.querySelector(`[data-window-id="${windowId}"] iframe`) as HTMLIFrameElement | null
    if (iframe) {
      // 使用窗口的 URL 重新加载
      const currentUrl = win.url
      iframe.src = ''
      setTimeout(() => {
        iframe.src = currentUrl
      }, 10)
    }
  })
}

const clearWindows = () => {
  if (confirm('确定要关闭所有窗口吗？')) {
    windows.value.clear()
    cancelUrlEdit()
  }
}

// ==================== 拖拽功能 ====================
const handleDetectionZoneMouseMove = (e: MouseEvent, win: WindowData) => {
  if (isDragging.value || isResizing.value) return
  e.stopPropagation()
  
  if (hoveredWindowId.value !== win.id) {
    hoveredWindowId.value = win.id
  }
}

const handleWindowMouseMove = (e: MouseEvent, win: WindowData) => {
  if (isDragging.value || isResizing.value) return
  
  const element = e.currentTarget as HTMLElement
  if (!element) return
  
  const rect = element.getBoundingClientRect()
  const relativeX = e.clientX - rect.left
  const relativeY = e.clientY - rect.top
  
  // 检测鼠标是否在窗口顶部中间区域
  // 顶部 80px 高度内，水平位置在窗口中间 20-80% 范围内
  // 从 DOM 读取窗口宽度，避免依赖响应式数据
  const width = rect.width
  const topThreshold = 80
  const centerStart = width * 0.2
  const centerEnd = width * 0.8
  
  if (
    relativeY <= topThreshold &&
    relativeX >= centerStart &&
    relativeX <= centerEnd
  ) {
    if (hoveredWindowId.value !== win.id) {
      hoveredWindowId.value = win.id
    }
  } else {
    if (hoveredWindowId.value === win.id && editingWindowId.value !== win.id) {
      hoveredWindowId.value = null
    }
  }
}

const handleWindowMouseLeave = (windowId: string) => {
  if (
    hoveredWindowId.value === windowId &&
    !isDragging.value &&
    !isResizing.value &&
    editingWindowId.value !== windowId
  ) {
    hoveredWindowId.value = null
  }
}

const handleGlobalPointerMove = (e: PointerEvent) => {
  if (!containerRef.value) return

  const rect = containerRef.value.getBoundingClientRect()
  const inside =
    e.clientX >= rect.left &&
    e.clientX <= rect.right &&
    e.clientY >= rect.top &&
    e.clientY <= rect.bottom

  if (!inside) {
    hoveredWindowId.value = null
    return
  }

  const targetWin = getWindowAtPosition(e.clientX, e.clientY)
  hoveredWindowId.value = targetWin ? targetWin.id : null
}

const handleGlobalMouseMove = (e: MouseEvent) => {
  if (!containerRef.value) return

  const rect = containerRef.value.getBoundingClientRect()
  const inside =
    e.clientX >= rect.left &&
    e.clientX <= rect.right &&
    e.clientY >= rect.top &&
    e.clientY <= rect.bottom

  if (!inside) {
    hoveredWindowId.value = null
    return
  }

  const targetWin = getWindowAtPosition(e.clientX, e.clientY)
  hoveredWindowId.value = targetWin ? targetWin.id : null
}

const focusUrlEditorInput = (windowId: string) => {
  nextTick(() => {
    const input = document.querySelector(`[data-url-editor="${windowId}"]`) as HTMLInputElement | null
    if (input) {
      input.focus()
      input.select()
    }
  })
}

const cancelUrlEdit = () => {
  editUrlValue.value = ''
  editingWindowId.value = null
}

const commitUrlEdit = () => {
  if (!editingWindowId.value) return
  const win = windows.value.get(editingWindowId.value)
  if (!win) {
    cancelUrlEdit()
    return
  }

  const nextUrl = editUrlValue.value.trim()
  if (nextUrl.length > 0) {
    win.url = nextUrl
  }

  cancelUrlEdit()
  hoveredWindowId.value = win.id
}

const toggleUrlEditor = (win: WindowData) => {
  if (editingWindowId.value === win.id) {
    cancelUrlEdit()
    return
  }

  editingWindowId.value = win.id
  editUrlValue.value = win.url
  hoveredWindowId.value = win.id
  focusUrlEditorInput(win.id)
}

const handleUrlInputChange = (event: Event) => {
  editUrlValue.value = (event.target as HTMLInputElement).value
}

const handleUrlInputKeydown = (event: KeyboardEvent) => {
  if (event.key === 'Enter') {
    event.preventDefault()
    commitUrlEdit()
  } else if (event.key === 'Escape') {
    event.preventDefault()
    cancelUrlEdit()
  }
}

const handleIslandPointerDown = (e: PointerEvent, win: WindowData) => {
  // 如果点击的是按钮，不触发拖动
  const target = e.target as HTMLElement
  if (target.closest('.island-button') || target.closest('.island-input')) {
    return
  }

  if (isDragging.value || isResizing.value) {
    e.preventDefault()
    return
  }

  e.preventDefault()
  e.stopPropagation()

  const island = e.currentTarget as HTMLElement | null
  const container = containerRef.value
  if (!island || !container) return

  hoveredWindowId.value = win.id
  if (editingWindowId.value) {
    cancelUrlEdit()
  }

  const pointerId = e.pointerId
  const containerRect = container.getBoundingClientRect()
  
  // 从 DOM 读取窗口位置，避免依赖响应式数据
  const windowElement = getWindowElement(win.id)
  if (!windowElement) return
  const windowRect = windowElement.getBoundingClientRect()
  const winX = windowRect.left - containerRect.left
  const winY = windowRect.top - containerRect.top
  const winWidth = windowRect.width
  
  const offsetX = e.clientX - (containerRect.left + winX)
  const offsetY = e.clientY - (containerRect.top + winY)

  isDragging.value = true
  draggingWindowId.value = win.id
  win.isDragging = true

  dragIndicator.value = {
    visible: true,
    x: winX,
    y: winY,
    width: winWidth,
    height: 40,
    title: win.title
  }

  document.body.style.userSelect = 'none'
  document.body.style.pointerEvents = 'none'
  document.body.style.cursor = 'move'
  container.style.pointerEvents = 'auto'

  const updateIndicator = (clientX: number, clientY: number) => {
    const newX = clientX - containerRect.left - offsetX
    const newY = clientY - containerRect.top - offsetY
    dragIndicator.value.x = newX
    dragIndicator.value.y = newY

    const targetWin = getWindowAtPosition(clientX, clientY)
    if (targetWin && targetWin.id !== win.id) {
      highlightedWindowId.value = targetWin.id
    } else {
      highlightedWindowId.value = null
    }
  }

  island.setPointerCapture(pointerId)
  updateIndicator(e.clientX, e.clientY)

  const onPointerMove = (ev: PointerEvent) => {
    if (ev.pointerId !== pointerId) return
    ev.preventDefault()
    updateIndicator(ev.clientX, ev.clientY)
  }

  const finishDrag = (ev: PointerEvent) => {
    if (ev.pointerId !== pointerId) return
    ev.preventDefault()
    ev.stopPropagation()

    island.removeEventListener('pointermove', onPointerMove)
    island.removeEventListener('pointerup', finishDrag)
    island.removeEventListener('pointercancel', finishDrag)

    if (island.hasPointerCapture(pointerId)) {
      island.releasePointerCapture(pointerId)
    }

    const wasDragging = isDragging.value
    const hadTarget = !!highlightedWindowId.value
    const targetWinId = highlightedWindowId.value

    isDragging.value = false
    draggingWindowId.value = null
    win.isDragging = false

    // 清除高亮状态
    highlightedWindowId.value = null
    dragIndicator.value.visible = false
    hoveredWindowId.value = null

    document.body.style.userSelect = ''
    document.body.style.pointerEvents = ''
    document.body.style.cursor = ''
    container.style.pointerEvents = ''

    if (wasDragging) {
      if (hadTarget && targetWinId) {
        // 有目标窗口，交换位置
        const targetWin = windows.value.get(targetWinId)
        if (targetWin) {
          // 交换 tileIndex（只更新索引，不更新位置数据）
          const tempIndex = win.tileIndex
          win.tileIndex = targetWin.tileIndex
          targetWin.tileIndex = tempIndex
          
          // 直接通过 CSS 交换两个窗口的位置，使用 transition 实现平滑动画
          const layout = calculateLayout(windows.value.size)
          const winTile = layout.tiles.find(t => t.index === win.tileIndex)
          const targetTile = layout.tiles.find(t => t.index === targetWin.tileIndex)
          
          if (winTile && targetTile) {
            const winElement = getWindowElement(win.id)
            const targetElement = getWindowElement(targetWin.id)
            
            if (winElement && targetElement) {
              // 设置 transition
              winElement.style.transition = 'left 0.3s ease-out, top 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out'
              targetElement.style.transition = 'left 0.3s ease-out, top 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out'
              
              // 交换位置
              winElement.style.left = `${winTile.x}px`
              winElement.style.top = `${winTile.y}px`
              winElement.style.width = `${winTile.width}px`
              winElement.style.height = `${winTile.height}px`
              
              targetElement.style.left = `${targetTile.x}px`
              targetElement.style.top = `${targetTile.y}px`
              targetElement.style.width = `${targetTile.width}px`
              targetElement.style.height = `${targetTile.height}px`
              
              // 动画完成后清除 transition
              setTimeout(() => {
                winElement.style.transition = ''
                targetElement.style.transition = ''
              }, 300)
            }
          }
        }
      } else {
        // 没有交换，恢复原位置（使用动画）
        applyLayout(true)
      }
    }
  }

  island.addEventListener('pointermove', onPointerMove)
  island.addEventListener('pointerup', finishDrag)
  island.addEventListener('pointercancel', finishDrag)
}

const getWindowAtPosition = (x: number, y: number): WindowData | null => {
  for (const win of windows.value.values()) {
    const element = getWindowElement(win.id)
    if (!element) continue
    const rect = element.getBoundingClientRect()
    if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
      return win
    }
  }
  return null
}

// ==================== 调整大小功能 ====================
const canResizeRight = (win: WindowData): boolean => findColumnAfter(win).length > 0

const canResizeBottom = (win: WindowData): boolean => findRowBelow(win).length > 0

const handleResizePointerDown = (e: PointerEvent, win: WindowData, edge: 'right' | 'bottom') => {
  if (isResizing.value) {
    e.preventDefault()
    return
  }

  e.preventDefault()
  e.stopPropagation()

  const handle = e.currentTarget as HTMLElement | null
  const container = containerRef.value
  if (!handle || !container) return

  if (editingWindowId.value) {
    cancelUrlEdit()
  }

  const buildTargets = (items: WindowData[]): ResizeTarget[] | null => {
    const targets: ResizeTarget[] = []
    const containerRect = container.getBoundingClientRect()
    for (const item of items) {
      const element = getWindowElement(item.id)
      if (!element) {
        return null
      }
      // 从 DOM 读取初始位置和大小，而不是从响应式数据读取
      const rect = element.getBoundingClientRect()
      targets.push({
        id: item.id,
        element,
        initialX: rect.left - containerRect.left,
        initialY: rect.top - containerRect.top,
        initialWidth: rect.width,
        initialHeight: rect.height
      })
    }
    return targets
  }

  const pointerId = e.pointerId

  const cleanupCommon = () => {
    isResizing.value = false
    resizingWindowId.value = null
    document.body.style.userSelect = ''
    document.body.style.pointerEvents = ''
    document.body.style.cursor = ''
    container.style.pointerEvents = ''
  }

  if (edge === 'right') {
    const columnTargets = buildTargets(findColumnPeers(win))
    const rightColumnTargets = buildTargets(findColumnAfter(win))

    if (!columnTargets || columnTargets.length === 0 || !rightColumnTargets || rightColumnTargets.length === 0) {
      return
    }

    const maxPositive = Math.max(0, Math.min(...rightColumnTargets.map(t => t.initialWidth - MIN_WINDOW_WIDTH)))
    const maxNegative = Math.max(0, Math.min(...columnTargets.map(t => t.initialWidth - MIN_WINDOW_WIDTH)))
    const startX = e.clientX

    isResizing.value = true
    resizingWindowId.value = win.id

    document.body.style.userSelect = 'none'
    document.body.style.pointerEvents = 'none'
    document.body.style.cursor = 'ew-resize'
    container.style.pointerEvents = 'auto'

    handle.setPointerCapture(pointerId)

    const onPointerMove = (ev: PointerEvent) => {
      if (ev.pointerId !== pointerId) return
      ev.preventDefault()

      let deltaX = ev.clientX - startX
      if (deltaX > 0) {
        deltaX = Math.min(deltaX, maxPositive)
      } else {
        deltaX = -Math.min(Math.abs(deltaX), maxNegative)
      }

      // 禁用 transition，确保调整大小实时响应
      columnTargets.forEach(target => {
        target.element.style.transition = 'none'
        const newWidth = Math.max(MIN_WINDOW_WIDTH, target.initialWidth + deltaX)
        target.element.style.width = `${newWidth}px`
      })

      rightColumnTargets.forEach(target => {
        target.element.style.transition = 'none'
        const newWidth = Math.max(MIN_WINDOW_WIDTH, target.initialWidth - deltaX)
        target.element.style.width = `${newWidth}px`
        target.element.style.left = `${target.initialX + deltaX}px`
      })
    }

    const finishResize = (ev: PointerEvent) => {
      if (ev.pointerId !== pointerId) return
      ev.preventDefault()
      ev.stopPropagation()

      handle.removeEventListener('pointermove', onPointerMove)
      handle.removeEventListener('pointerup', finishResize)
      handle.removeEventListener('pointercancel', finishResize)

      if (handle.hasPointerCapture(pointerId)) {
        handle.releasePointerCapture(pointerId)
      }

      // 恢复 transition（清除之前设置的 'none'）
      columnTargets.forEach(target => {
        target.element.style.transition = ''
      })
      rightColumnTargets.forEach(target => {
        target.element.style.transition = ''
      })

      // 注意：不更新响应式数据，位置和大小完全由 CSS 控制
      // 这样可以避免触发 Vue 重新渲染，防止 iframe 重新加载

      cleanupCommon()
    }

    handle.addEventListener('pointermove', onPointerMove)
    handle.addEventListener('pointerup', finishResize)
    handle.addEventListener('pointercancel', finishResize)
    return
  }

  const rowTargets = buildTargets(findRowPeers(win))
  const lowerRowTargets = buildTargets(findRowBelow(win))

  if (!rowTargets || rowTargets.length === 0 || !lowerRowTargets || lowerRowTargets.length === 0) {
    return
  }

  const maxPositive = Math.max(0, Math.min(...lowerRowTargets.map(t => t.initialHeight - MIN_WINDOW_HEIGHT)))
  const maxNegative = Math.max(0, Math.min(...rowTargets.map(t => t.initialHeight - MIN_WINDOW_HEIGHT)))
  const startY = e.clientY

  isResizing.value = true
  resizingWindowId.value = win.id

  document.body.style.userSelect = 'none'
  document.body.style.pointerEvents = 'none'
  document.body.style.cursor = 'ns-resize'
  container.style.pointerEvents = 'auto'

  handle.setPointerCapture(pointerId)

    const onPointerMove = (ev: PointerEvent) => {
      if (ev.pointerId !== pointerId) return
      ev.preventDefault()

      let deltaY = ev.clientY - startY
      if (deltaY > 0) {
        deltaY = Math.min(deltaY, maxPositive)
      } else {
        deltaY = -Math.min(Math.abs(deltaY), maxNegative)
      }

      // 禁用 transition，确保调整大小实时响应
      rowTargets.forEach(target => {
        target.element.style.transition = 'none'
        const newHeight = Math.max(MIN_WINDOW_HEIGHT, target.initialHeight + deltaY)
        target.element.style.height = `${newHeight}px`
      })

      lowerRowTargets.forEach(target => {
        target.element.style.transition = 'none'
        const newHeight = Math.max(MIN_WINDOW_HEIGHT, target.initialHeight - deltaY)
        target.element.style.height = `${newHeight}px`
        target.element.style.top = `${target.initialY + deltaY}px`
      })
    }

  const finishResize = (ev: PointerEvent) => {
    if (ev.pointerId !== pointerId) return
    ev.preventDefault()
    ev.stopPropagation()

    handle.removeEventListener('pointermove', onPointerMove)
    handle.removeEventListener('pointerup', finishResize)
    handle.removeEventListener('pointercancel', finishResize)

    if (handle.hasPointerCapture(pointerId)) {
      handle.releasePointerCapture(pointerId)
    }

    // 恢复 transition（清除之前设置的 'none'）
    rowTargets.forEach(target => {
      target.element.style.transition = ''
    })
    lowerRowTargets.forEach(target => {
      target.element.style.transition = ''
    })

    // 注意：不更新响应式数据，位置和大小完全由 CSS 控制
    // 这样可以避免触发 Vue 重新渲染，防止 iframe 重新加载
    // 调整大小后，不调用 applyLayout，保持手动调整的状态

    cleanupCommon()
  }

  handle.addEventListener('pointermove', onPointerMove)
  handle.addEventListener('pointerup', finishResize)
  handle.addEventListener('pointercancel', finishResize)
}

// ==================== 生命周期 ====================
const updateContainerSize = () => {
  if (containerRef.value) {
    const rect = containerRef.value.getBoundingClientRect()
    containerRect.value = { width: rect.width, height: rect.height }
    applyLayout()
  }
}

let resizeObserver: ResizeObserver | null = null

onMounted(() => {
  updateContainerSize()
  
  // 使用 ResizeObserver 监听容器大小变化
  if (containerRef.value) {
    resizeObserver = new ResizeObserver(() => {
      updateContainerSize()
    })
    resizeObserver.observe(containerRef.value)
  }
  
  // 添加测试窗口
  addWindow()

  window.addEventListener('pointermove', handleGlobalPointerMove, { passive: true })
  window.addEventListener('mousemove', handleGlobalMouseMove, { passive: true, capture: true })
})

onUnmounted(() => {
  if (resizeObserver && containerRef.value) {
    resizeObserver.unobserve(containerRef.value)
  }

  window.removeEventListener('pointermove', handleGlobalPointerMove)
  window.removeEventListener('mousemove', handleGlobalMouseMove, true)
})
</script>

<style scoped>
.test-page {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #1e1e1e;
  color: #d4d4d4;
  font-family: 'Ubuntu Mono', 'Consolas', monospace;
}

.test-header {
  height: 60px;
  background: #252526;
  border-bottom: 1px solid #007acc;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  flex-shrink: 0;
}

.test-header h1 {
  margin: 0;
  font-size: 18px;
  font-weight: 500;
  color: #007acc;
}

.test-controls {
  display: flex;
  gap: 10px;
}

.btn-primary,
.btn-secondary {
  padding: 8px 16px;
  border: 1px solid #007acc;
  background: transparent;
  color: #007acc;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s;
  font-family: inherit;
}

.btn-primary:hover {
  background: #007acc;
  color: #fff;
}

.btn-secondary {
  border-color: #666;
  color: #999;
}

.btn-secondary:hover {
  background: #333;
  color: #fff;
  border-color: #999;
}

/* 窗口容器 */
.window-container {
  flex: 1;
  position: relative;
  overflow: visible;
  background: #1e1e1e;
}

/* 窗口 */
.window {
  position: absolute;
  background: white; /* 改为白色，避免影响 iframe 内容 */
  border: 1px solid #e0e0e0; /* 改为浅色边框 */
  display: flex;
  flex-direction: column;
  transition: box-shadow 0.2s;
  will-change: transform;
  overflow: visible;
}

.window-highlighted {
  border-color: #007acc;
  box-shadow: 0 0 10px rgba(0, 122, 204, 0.5);
}

.window-dragging {
  opacity: 0.5;
}

/* 窗口标题栏 */
.window-header {
  display: none;
}

/* 检测区域可视化 */
.hover-detection-zone {
  background: rgba(255, 100, 100, 0.2);
  border: 1px dashed rgba(255, 100, 100, 0.4);
}

/* iOS灵动岛样式 */
.window-island {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -100%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 1px 8px;
  max-width: calc(100% - 16px);
  background: rgba(37, 37, 38, 0.85);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 0.5px solid rgba(255, 255, 255, 0.1);
  border-radius: 0;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), inset 0 0 0 0.5px rgba(255, 255, 255, 0.05);
  opacity: 0;
  pointer-events: none;
  cursor: move;
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease-out;
  z-index: 1000;
  white-space: nowrap;
}

.window-island.island-hovered,
.window-island.island-expanded {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, 0);
}

.window-island.island-expanded {
  padding: 1px 10px;
}

.island-content {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.island-editor {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s ease;
  flex-shrink: 0;
}

.island-editor.island-editor-visible {
  max-width: 300px;
  opacity: 1;
}

.island-button {
  width: 20px;
  height: 12px;
  padding: 0;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  transition: background 0.15s ease, color 0.15s ease, transform 0.1s ease;
  flex-shrink: 0;
}

.island-button:hover {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.9);
}

.island-button:active {
  transform: scale(0.9);
}

.island-button svg {
  width: 12px;
  height: 12px;
  stroke: currentColor;
  stroke-width: 1.5;
}

.island-edit[aria-pressed='true'] {
  background: rgba(59, 124, 209, 0.2);
  color: rgba(59, 124, 209, 1);
}

.island-refresh:hover {
  background: rgba(76, 175, 80, 0.15);
  color: rgba(76, 175, 80, 1);
}

.island-close:hover {
  background: rgba(255, 82, 82, 0.15);
  color: rgba(255, 82, 82, 1);
}

.island-input {
  flex: 1;
  min-width: 0;
  height: 12px;
  padding: 0 6px;
  border: none;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.9);
  font-size: 10px;
  line-height: 1.2;
  border-radius: 2px;
  outline: none;
  transition: background 0.15s ease;
}

.island-input::placeholder {
  color: rgba(255, 255, 255, 0.4);
}

.island-input:focus {
  background: rgba(255, 255, 255, 0.12);
}

.island-action.island-commit:hover {
  background: rgba(59, 124, 209, 0.2);
  color: rgba(59, 124, 209, 1);
}

.island-action.island-cancel:hover {
  background: rgba(255, 82, 82, 0.15);
  color: rgba(255, 82, 82, 1);
}

.window-title {
  font-size: 13px;
  color: #cccccc;
  font-weight: 500;
}

.window-close {
  width: 24px;
  height: 24px;
  border: none;
  background: transparent;
  color: #cccccc;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
}

.window-close:hover {
  background: #e81123;
  color: #fff;
}

/* 窗口内容区 */
.window-content {
  flex: 1;
  overflow: hidden;
  background: white; /* 改为白色，避免影响 iframe 内容 */
  position: relative;
}

.window-iframe {
  width: 100%;
  height: 100%;
  border: none;
  display: block;
  /* 防止iframe在拖动时捕获鼠标事件 */
  pointer-events: auto;
  background: white; /* 确保 iframe 背景是白色 */
}

/* 调整大小句柄 */
.resize-handle {
  position: absolute;
  background: transparent;
  z-index: 10;
  transition: background 0.15s;
}

.resize-handle-right {
  right: -6px;
  top: 0;
  bottom: 0;
  width: 12px;
  cursor: ew-resize;
}

.resize-handle-right::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  background: rgba(0, 122, 204, 0.3);
  transform: translateX(-50%);
  transition: background 0.15s;
}

.resize-handle-right:hover::after {
  background: #007acc;
  width: 3px;
}

.resize-handle-bottom {
  left: 0;
  right: 0;
  bottom: -6px;
  height: 12px;
  cursor: ns-resize;
}

.resize-handle-bottom::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  background: rgba(0, 122, 204, 0.3);
  transform: translateY(-50%);
  transition: background 0.15s;
}

.resize-handle-bottom:hover::after {
  background: #007acc;
  height: 3px;
}

.resize-handle:active::after {
  background: #00a8ff !important;
}

/* 拖拽指示器 */
.drag-indicator {
  position: absolute;
  background: rgba(0, 122, 204, 0.3);
  border: 2px dashed #007acc;
  color: #007acc;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  pointer-events: none;
  z-index: 1000;
  font-size: 14px;
}
</style>
